from django.urls import reverse
from utils.bootstrap import BootStrapForm
from django import forms
from django.shortcuts import render, redirect
from django.urls import path
from django.db.models import Q
from django.shortcuts import HttpResponse
from utils.pager import Pagination
from www import models
from django.utils.safestring import mark_safe, SafeData
from utils.encrypt import md5_string
from urllib.parse import quote_plus

"""
使用说明：
按此编写
def demo(request):
    # 只支持FK组件："level"  "creator"
    group = SearchGroup(models.Customer, request,
                        Option('mobile', is_multi=False, is_choice=False, db_condition={'id__lte': 11},
                               text_func=lambda x: x.mobile, value_func=lambda x: x.mobile),
                        Option('level', is_multi=False, db_condition={'active': 1}),
                        Option('creator', is_multi=True, ),
                        Option('gender', is_multi=False, is_choice=True, text_func=lambda x: str(x[1]),
                               value_func=lambda x: str(x[0])),
                        Option('hbs', is_multi=True)
                        )
    row_list = group.get_row_list()
    condition = group.get_condition()
    queryset = models.Customer.objects.filter(**condition).order_by('-id')
    # pager = Pagination(request, queryset)
    return render(request, 'v2.html', {"queryset": queryset, "row_list": row_list})


    在HTML中
    <link rel="stylesheet" href="{% static 'css/search-group.css' %}">
    {% include 'include/search_group.html' %}
"""
from django.db.models import ForeignKey, ManyToManyField


class Row():
    def __init__(self, opt_obj, title, queryset_or_list, request):
        self.opt_obj = opt_obj
        self.title = title
        self.queryset_or_list = queryset_or_list
        self.query_dict = request.GET

    def __iter__(self):
        # self.opt_obj.is_multi
        query_dict = self.query_dict.copy()
        loop_query_dict = self.query_dict.copy()
        query_dict._mutable = True
        loop_query_dict._mutable = True
        yield '<div class="whole">'
        yield self.title
        yield '</div>'
        yield '<div class="other">'
        value_list = query_dict.getlist(self.opt_obj.field)
        if value_list:
            query_dict.pop(self.opt_obj.field)
            yield f'<a href="?{query_dict.urlencode()}">全部</a>'
        else:
            yield f'<a class="active" href="?{query_dict.urlencode()}">全部</a>'
        for obj in self.queryset_or_list:
            # if self.opt_obj.is_choice:
            #     text = obj[1]
            #     value = str(obj[0])
            # else:
            #     text = str(obj)
            #     value = str(obj.pk)
            # text = self.opt_obj.text_func(obj)
            # value = self.opt_obj.value_func(obj)
            text = self.opt_obj.get_text(obj)
            value = self.opt_obj.get_value(obj)
            if self.opt_obj.is_multi:
                # loop_query_dict._mutable = True
                multi_list = loop_query_dict.getlist(self.opt_obj.field)
                if (value in multi_list):
                    multi_list.remove(value)
                    query_dict.setlist(self.opt_obj.field, multi_list)
                    url = query_dict.urlencode()
                    yield f"<a class='active' href='?{url}'>{text}</a>"
                else:
                    multi_list.append(value)
                    query_dict.setlist(self.opt_obj.field, multi_list)
                    url = query_dict.urlencode()
                    yield f"<a href='?{url}'>{text}</a>"
            else:
                # loop_query_dict._mutable = True
                value_list = loop_query_dict.getlist(self.opt_obj.field)
                query_dict.setlist(self.opt_obj.field, [value])

                url = query_dict.urlencode()
                if value in value_list:
                    yield f"<a class='active' href='?{url}'>{text}</a>"
                else:
                    yield f"<a href='?{url}'>{text}</a>"
        yield '</div>'


class SearchGroup(object):
    def __init__(self, request, model_class, *options):
        self.model_class = model_class
        self.request = request
        self.options = options

    def get_row_list(self):
        row_list = []
        for opt_obj in self.options:
            field_object = self.model_class._meta.get_field(opt_obj.field)
            # (field_object.choices)
            title = field_object.verbose_name
            if opt_obj.is_choice:
                queryset_or_list = field_object.choices
            else:
                if isinstance(field_object, ForeignKey) or isinstance(field_object, ManyToManyField):
                    queryset_or_list = opt_obj.get_queryset(field_object.related_model, self.request)
                else:
                    #         获取当前表中的数据
                    queryset_or_list = self.model_class.objects.filter(**opt_obj.db_condition)
            obj = Row(opt_obj, title, queryset_or_list, self.request)
            row_list.append(obj)
        return row_list

    def get_condition(self):
        condition = {}
        for opt_obj in self.options:
            field, value = opt_obj.get_search_condition(self.request)
            if not value:
                continue
            condition[field] = value
        return condition


class Option(object):
    def __init__(self, field, is_multi=False, db_condition=None, is_choice=False, text_func=None, value_func=None):
        self.field = field
        self.is_multi = is_multi
        if not db_condition:
            db_condition = {}
        self.db_condition = db_condition
        self.is_choice = is_choice
        self.text_func = text_func
        self.value_func = value_func

    def get_search_condition(self, request):
        if self.is_multi:
            value = request.GET.getlist(self.field)
            return f"{self.field}__in", value
        else:
            value = request.GET.get(self.field)
            return f"{self.field}", value

    def get_queryset(self, related_model, request):
        return related_model.objects.filter(**self.db_condition)

    def get_text(self, obj_or_tuple):
        if self.text_func:
            return self.text_func(obj_or_tuple)
        if self.is_choice:
            return obj_or_tuple[1]
        return str(obj_or_tuple)

    def get_value(self, obj_or_tuple):
        if self.value_func:
            return self.value_func(obj_or_tuple)
        if self.is_choice:
            return str(obj_or_tuple[0])
        return str(obj_or_tuple.pk)


class BaseCurd(object):
    list_template_name = 'v3_list.html'
    add_template = 'form2.html'
    edit_template = 'form2.html'
    search_field_list = []
    search_group = []

    def __init__(self, view_list, prefix):
        self.view_list = view_list
        self.prefix = prefix

    def dispatch(self, func):
        def inner(request, *args, **kwargs):
            self.request = request
            res = func(request, *args, **kwargs)
            # try:
            #     res = func(request, *args, **kwargs)
            # except Exception as e:
            #     res = HttpResponse("异常")
            return res

        return inner

    @classmethod
    def urls(cls, view_list=None, prefix=None):
        if not view_list:
            view_list = ['list', 'add', 'edit', 'delete']
        if not prefix:
            prefix = cls.__name__.lower()
        instance = cls(view_list, prefix)

        router = []
        for item in view_list:
            func = getattr(instance, item)
            if item in ['list', 'add']:
                router.append(path(f'{item}/', instance.dispatch(func), name=f'{prefix}_{item}'))
            else:
                router.append(path(f'{item}/<int:pk>/', instance.dispatch(func), name=f'{prefix}_{item}'))

        extra_router = instance.get_extra_url()
        if extra_router:
            router.extend(extra_router)
        return (router, None, None)

    def get_extra_url(self):
        return None

    def get_queryset(self, request):
        return self.queryset

    def list(self, request):
        queryset = self.get_queryset(request)
        keyword = request.GET.get('keyword', "").strip()
        if self.search_field_list:
            con = Q()
            if keyword:
                con.connector = 'OR'
                for search_field in self.search_field_list:
                    con.children.append((search_field, keyword))
                queryset = queryset.filter(con)
        search_row_list = []
        if self.search_group:
            group = SearchGroup(
                request,
                *self.search_group
            )
            queryset = queryset.filter(**group.get_condition())
            search_row_list = group.get_row_list()
        pager = Pagination(request, queryset)
        context = {
            'pager': pager,
            'table_header': self.table_header,
            'table_field': self.table_field,
            'url_add_name': f"{self.prefix}_add",
            'url_edit_name': f"{self.prefix}_edit",
            'url_delete_name': f"{self.prefix}_delete",
            'search_field_list': self.search_field_list,
            'keyword': keyword,
            'search_group': self.search_group,
            'search_row_list': search_row_list,
        }
        return render(request, self.list_template_name, context)

    def add(self, request):
        form_class = self.add_form_class
        origin = request.GET.get('redirect')
        if request.method == 'GET':
            form = form_class()
            return render(request, self.add_template, {'form': form})
        form = form_class(data=request.POST)
        if not form.is_valid():
            return render(request, self.add_template, {'form': form})

        self.add_save(form)

        return redirect(origin)

    def add_save(self, form):
        form.save()

    def edit(self, request, pk):
        form_class = self.edit_form_class
        origin = request.GET.get('redirect')
        instance = self.get_instance(request, pk)
        if request.method == 'GET':
            form = form_class(instance=instance)
            return render(request, self.edit_template, {'form': form})
        form = form_class(data=request.POST, instance=instance)
        if not form.is_valid():
            return render(request, self.edit_template, {'form': form})
        self.edit_save(form)

        return redirect(origin)

    def edit_save(self, form):
        form.save()

    def get_instance(self, request, pk):
        return None

    def delete(self, request, pk):
        origin = request.GET.get('redirect')
        if request.method == 'GET':
            return render(request, 'delete.html', {"origin": origin})
        self.do_delete(request, pk)
        return redirect(origin)

    def do_delete(self, request, pk):
        pass


class CustomerModelForm(BootStrapForm, forms.ModelForm):
    exclude_field_list = ['level']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['level'].queryset = models.Level.objects.filter(active=1)

    password = forms.CharField(widget=forms.PasswordInput(render_value=True), label='密码')
    password_repeat = forms.CharField(widget=forms.PasswordInput(render_value=True), label='确认密码')

    class Meta:
        model = models.Customer
        fields = ['level', 'username', 'mobile', 'password', 'password_repeat']
        widgets = {
            'level': forms.RadioSelect(attrs={'class': 'form-radio'}),  # 设置 level 字段的默认组件类型,
        }

    def clean_password(self):
        password = self.cleaned_data['password']
        md5_pwd = md5_string(password)
        return md5_pwd

    def clean_password_repeat(self):
        password = self.cleaned_data['password']
        password_repeat = md5_string(self.cleaned_data['password_repeat'])
        if password != password_repeat:
            raise forms.ValidationError('密码不一致')
        md5_pwd_repeat = md5_string(password_repeat)
        return md5_pwd_repeat


class CustomerEditModelForm(BootStrapForm, forms.ModelForm):
    exclude_field_list = ['level']

    class Meta:
        model = models.Customer
        fields = ['level', 'username', 'mobile']
        widgets = {
            'level': forms.RadioSelect(attrs={'class': 'form-radio'})
        }


class CustomerResetModelForm(BootStrapForm, forms.ModelForm):
    password_repeat = forms.CharField(widget=forms.PasswordInput(render_value=True), label='确认密码', required=False)
    password = forms.CharField(widget=forms.PasswordInput(render_value=True), label='密码', required=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 隐藏密码和确认密码字段
        self.initial['password'] = ''

    def clean_password(self):
        password = md5_string(self.cleaned_data['password'])
        return password

    def clean_password_repeat(self):
        password_repeat = md5_string(self.cleaned_data['password_repeat'])
        password = self.cleaned_data['password']
        if password != password_repeat:
            raise forms.ValidationError('两次密码不一致')
        return password_repeat

    class Meta:
        model = models.Customer
        fields = ['username', 'password', 'password_repeat']
        widgets = {
            'username': forms.TextInput(attrs={'readonly': True})  # 设置用户名为只读
        }


class V3(BaseCurd):
    queryset = models.Customer.objects.filter(active=1).order_by('-id')
    add_form_class = CustomerModelForm
    edit_form_class = CustomerEditModelForm
    add_template = 'form.html'
    edit_template = 'form.html'

    search_field_list = ['username', 'mobile__contains']
    table_header = ['用户名', '手机号', '级别', '创建日期', '重置密码']

    search_group = [
        models.Customer,
        Option('level', is_multi=False, db_condition={'active': 1}),
        Option('creator', is_multi=True, ),
        Option('gender', is_multi=False, is_choice=True, text_func=lambda x: str(x[1]),
               value_func=lambda x: str(x[0])),
        Option('mobile', is_multi=False, is_choice=False, db_condition={'id__lte': 11},
               text_func=lambda x: x.mobile, value_func=lambda x: x.mobile),
        # Option('hbs', is_multi=True)
    ]

    def get_instance(self, request, pk):
        return models.Customer.objects.filter(id=pk, active=1).first()

    def add_save(self, form):
        form.instance.creator_id = self.request.nb_user.id
        form.save()

    def get_level(request, instance):
        return f"{instance.level.title}（{instance.level.percent}%折扣）"

    def get_create_date(request, instance):
        return instance.create_date.strftime('%Y-%m-%d')

    def reset_password(request, instance):
        url = reverse(viewname=f"customer4_reset", kwargs={'pk': instance.id})
        current_url = request.get_full_path()
        param = quote_plus(current_url)
        return mark_safe(f"<a href='{url}?redirect={param}'>重置密码</>")

    table_field = [('field', 'username'), ('field', 'mobile'), ('func', get_level), ('func', get_create_date),
                   ('func', reset_password)]

    def do_delete(self, request, pk):
        models.Customer.objects.filter(id=pk, active=1).update(active=0)

    def get_extra_url(self):
        return [
            path(f'reset/<int:pk>/', self.dispatch(self.reset), name=f"{self.prefix}_reset")
        ]

    def reset(self, request, pk):
        origin = request.GET.get('redirect', '/home/')
        instance = models.Customer.objects.filter(id=pk).first()
        # initial_data = {'username': instance.username}
        if request.method == 'GET':
            form = CustomerResetModelForm(instance=instance)
            return render(request, 'form.html', {"form": form})
        form = CustomerResetModelForm(data=request.POST, instance=instance)
        if not form.is_valid():
            return render(request, 'form.html', {'form': form})
        form.save()
        return redirect(origin)


# ################ 部门管理 ################
class DepartModelForm(BootStrapForm, forms.ModelForm):
    class Meta:
        model = models.Depart
        fields = ['title']


class Depart(BaseCurd):
    queryset = models.Depart.objects.all().order_by("-id")
    print(queryset)
    # search_list = ["username", "mobile__contains"]
    table_header = ["ID", "名称"]
    table_field = [("field", "id"), ("field", "title")]

    add_form_class = DepartModelForm
    edit_form_class = DepartModelForm

    def get_instance(self, request, nid):
        return models.Depart.objects.filter(id=nid).first()

    def do_delete(self, request, pk):
        models.Depart.objects.filter(id=pk).delete()

class InfoModelForm(BootStrapForm, forms.ModelForm):
    class Meta:
        model = models.Info
        fields = ['username','gender','depart']


class Info(BaseCurd):
    queryset = models.Info.objects.all().order_by("-id")
    print(queryset)
    # search_list = ["username", "mobile__contains"]
    table_header = ["ID", "名称","性别",'部门']
    def get_gender_text(request, obj):
        return obj.get_gender_display()


    def get_depart_text(request, obj):
        return obj.depart.title


    table_field = [("field", "id"), ("field", "username"),("func", get_gender_text), ("func", get_depart_text)]

    add_form_class = InfoModelForm
    edit_form_class = InfoModelForm

    def get_instance(self, request, nid):
        return models.Info.objects.filter(id=nid).first()

    def do_delete(self, request, pk):
        models.Depart.Info.filter(id=pk).delete()